{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# Cell\n",
    "\n",
    "A `@cell` is a decorator for functions that return a Component. Make sure you add the `@cell` decorator to each function that returns a Component so you avoid having multiple components with the same name.\n",
    "\n",
    "Why do you need to add the `@cell` decorator?\n",
    "\n",
    "- In GDS each component must have a unique name. Ideally the name is also consistent from run to run, in case you want to merge GDS files that were created at different times or computers.\n",
    "- Two components stored in the GDS file cannot have the same name. They need to be references (instances) of the same component. See `References tutorial`. That way we only have to store the component in memory once and all the references are just pointers to that component.\n",
    "\n",
    "What does the `@cell` decorator do?\n",
    "\n",
    "1. Gives the component a unique name depending on the parameters that you pass to it.\n",
    "2. Creates a cache of components where we use the name as the key. The first time the function runs, the cache stores the component, so the second time, you get the component directly from the cache, so you do not create the same component twice.\n",
    "\n",
    "\n",
    "What is a decorator?\n",
    "\n",
    "A decorator is a function that runs over another function, so when you do:\n",
    "\n",
    "```python\n",
    "@gf.cell\n",
    "def mzi_with_bend():\n",
    "    c = gf.Component()\n",
    "    mzi = c << gf.components.mzi()\n",
    "    bend = c << gf.components.bend_euler()\n",
    "    return c\n",
    "```\n",
    "it's equivalent to:\n",
    "\n",
    "```python\n",
    "def mzi_with_bend():\n",
    "    c = gf.Component()\n",
    "    mzi = c << gf.components.mzi()\n",
    "    bend = c << gf.components.bend_euler(radius=radius)\n",
    "    return c\n",
    "\n",
    "\n",
    "mzi_with_bend_decorated = gf.cell(mzi_with_bend)\n",
    "```\n",
    "\n",
    "Let us see how it works."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "\n",
    "def mzi_with_bend(radius: float = 10.0) -> gf.Component:\n",
    "    c = gf.Component()\n",
    "    mzi = c << gf.components.mzi()\n",
    "    bend = c << gf.components.bend_euler(radius=radius)\n",
    "    bend.connect(\"o1\", mzi.ports[\"o2\"])\n",
    "    return c\n",
    "\n",
    "\n",
    "c = mzi_with_bend()\n",
    "print(f\"this cell {c.name!r} does NOT get automatic name\")\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "mzi_with_bend_decorated = gf.cell(mzi_with_bend)\n",
    "c = mzi_with_bend_decorated(radius=10)\n",
    "print(f\"this cell {c.name!r} gets automatic name thanks to the `cell` decorator\")\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3",
   "metadata": {},
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def mzi_with_bend(radius: float = 10.0) -> gf.Component:\n",
    "    c = gf.Component()\n",
    "    mzi = c << gf.components.mzi()\n",
    "    bend = c << gf.components.bend_euler(radius=radius)\n",
    "    bend.connect(\"o1\", mzi.ports[\"o2\"])\n",
    "    return c\n",
    "\n",
    "\n",
    "print(f\"this cell {c.name!r} gets automatic name thanks to the `cell` decorator\")\n",
    "c.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# See how the cells get the name from the parameters that you pass them.\n",
    "\n",
    "c = mzi_with_bend()\n",
    "print(c)\n",
    "\n",
    "print(\"second time you get this cell from the cache\")\n",
    "c = mzi_with_bend()\n",
    "print(c)\n",
    "\n",
    "print(\"If you call the cell with different parameters, the cell gets a different name\")\n",
    "c = mzi_with_bend(radius=20)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "Sometimes when you are changing the inside code of the function, you need to remove the component from the cache to make sure the code runs again.\n",
    "\n",
    "This is useful when using jupyter notebooks or the file watcher."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "@gf.cell\n",
    "def wg(length=10, width=1, layer=(1, 0)):\n",
    "    print(\"BUILDING waveguide\")\n",
    "    c = gf.Component()\n",
    "    c.info[\"area\"] = width * length\n",
    "    c.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=layer)\n",
    "    c.add_port(\n",
    "        name=\"o1\", center=(0, width / 2), width=width, orientation=180, layer=layer\n",
    "    )\n",
    "    c.add_port(\n",
    "        name=\"o2\", center=(length, width / 2), width=width, orientation=0, layer=layer\n",
    "    )\n",
    "    return c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "c = wg()\n",
    "gf.clear_cache()\n",
    "c = wg()\n",
    "gf.clear_cache()\n",
    "c = wg()\n",
    "gf.clear_cache()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8",
   "metadata": {},
   "source": [
    "Another option is to just delete the cell after creating it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = wg(length=11)\n",
    "c.delete()\n",
    "c = wg(length=11)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10",
   "metadata": {},
   "source": [
    "## Settings and Info\n",
    "\n",
    "Together with the GDS file that you send to the foundry you can also store the settings for each component.\n",
    "\n",
    "- `Component.settings` are the input settings for each cell to generate a component. For example, `wg.settings.length` will return the input `length` setting to you, which you used to create that waveguide.\n",
    "- `Component.info` are the derived properties that will be computed inside the cell function. For example `wg.info.area` will return the computed area of that waveguide.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "#.info: This is an attribute of the component that acts like a dictionary. \n",
    "# It is a place to store extra, non-geometrical information about the component, such as test data, measurement results, or documentation links.\n",
    "c.info"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "c.info['area']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "# .settings: This is an attribute of the component that stores a dictionary of all the parameters that were used to create it. \n",
    "# This is how gdsfactory keeps track of the properties of a parametric cell (PCell).\n",
    "c.settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "c.settings['length']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "Components also have pretty print for ports `c.pprint_ports()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "c.pprint_ports()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# You always add any relevant information `info` to the cell.\n",
    "c.info[\"polarization\"] = \"te\"\n",
    "c.info[\"wavelength\"] = 1.55\n",
    "c.info"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "## Cache\n",
    "\n",
    "To avoid that 2 exact cells are not references of the same cell the `cell` decorator has a cache where if a component has already been built it will return the component from the cache"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# @gf.cell: This decorator registers the straight function as a parametric cell generator.\n",
    "#  A key feature it enables is caching; gdsfactory will store the generated component so it does not have to rebuild it if called again with the same parameters.\n",
    "@gf.cell\n",
    "def straight(length=10, width=1):\n",
    "    c = gf.Component()\n",
    "    c.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=(1, 0))\n",
    "    # This line prints a message to the console, including the length of the waveguide being built.\n",
    "    print(f\"BUILDING {length}um long straight waveguide\")\n",
    "    return c"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "If you run the cell below multiple times it will print a message because we are deleting the CACHE every single time and every time the cell will have a different Unique Identifier (UID)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "wg1 = straight()\n",
    "id(wg1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22",
   "metadata": {},
   "source": [
    "If you run the cell below multiple times it will NOT print a message because we are hitting CACHE every single time and every time the cell will have the SAME Unique Identifier (UID) because it's the same cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "wg2 = straight(length=10)\n",
    "# Cell returns the same straight as before without having to run the function.\n",
    "print(id(wg2))  # Notice that they have the same uuid (unique identifier)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24",
   "metadata": {},
   "outputs": [],
   "source": [
    "wg3 = straight(length=12)\n",
    "wg4 = straight(length=13)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25",
   "metadata": {},
   "source": [
    "## Create cells without `cell` decorator\n",
    "\n",
    "The cell decorator names cells deterministically and uniquely based on the name of the functions and its parameters.\n",
    "\n",
    "It also uses a caching mechanism that improves performance and guards against duplicated names.\n",
    "\n",
    "The most common mistake new gdsfactory users make is to create cells without the `cell` decorator.\n",
    "\n",
    "### Avoid naming cells manually: Use cell decorator\n",
    "\n",
    "Naming cells manually is susceptible to name collisions\n",
    "\n",
    "in GDS you can't have two cells with the same name.\n",
    "\n",
    "For example: this code will raise a `duplicated cell name ValueError`\n",
    "\n",
    "```python\n",
    "import gdsfactory as gf\n",
    "\n",
    "c1 = gf.Component(\"wg\")\n",
    "c1 << gf.components.straight(length=5)\n",
    "\n",
    "\n",
    "c2 = gf.Component(\"wg\")\n",
    "c2 << gf.components.straight(length=50)\n",
    "\n",
    "\n",
    "c3 = gf.Component(\"waveguides\")\n",
    "wg1 = c3 << c1\n",
    "wg2 = c3 << c2\n",
    "wg2.movey(10)\n",
    "c3\n",
    "```\n",
    "\n",
    "**Solution**: Use the `gf.cell` decorator for automatic naming your components."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "\n",
    "@gf.cell    \n",
    "def wg(length: float = 3):\n",
    "    return gf.components.straight(length=length)\n",
    "\n",
    "# Clear the cache to ensure that the components will be built fresh, not loaded from a previous run.\n",
    "gf.clear_cache()\n",
    "\n",
    "# This deterministic naming scheme ensures that every geometrically unique component has a unique name, \n",
    "# which is essential for creating reliable and reproducible designs.\n",
    "print(wg(length=5).name) # The name attribute becomes wg_length5.\n",
    "print(wg(length=10).name) # The name attribute becomes wg_length10."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27",
   "metadata": {},
   "source": [
    "### Avoid Unnamed cells. Use `cell` decorator\n",
    "\n",
    "In the case of not wrapping the function with `cell` you will get unique names thanks to the unique identifier `uuid`.\n",
    "\n",
    "This name will be different and non-deterministic for different invocations of the script.\n",
    "\n",
    "However it will be hard for you to know where that cell came from."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "c1 = gf.Component()\n",
    "c2 = gf.Component()\n",
    "\n",
    "print(c1.name)\n",
    "print(c2.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29",
   "metadata": {},
   "source": [
    "Notice how gdsfactory raises a Warning when you save this `Unnamed` Component."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "# .write_gds(): This is a method that takes the component's geometry and writes it to a GDSII (.gds) file.\n",
    "# This file is the final \"blueprint\" that is sent to a foundry for manufacturing.\n",
    "c1.write_gds() "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "### Avoid Intermediate Unnamed cells. Use `cell` decorator\n",
    "\n",
    "While creating a cell, you should not create intermediate cells, because they will not be Cached and you can end up with duplicated cell names or name conflicts, where one of the cells that has the same name as the other will be replaced.\n",
    "\n",
    "\n",
    "```python\n",
    "\n",
    "@gf.cell\n",
    "def die_bad():\n",
    "    \"\"\"c1 is an intermediate Unnamed cell\"\"\"\n",
    "    c1 = gf.Component() # This is an intermediate cell, it is not named as it is not returned by the function.\n",
    "    _ = c1 << gf.components.straight(length=10)\n",
    "    return gf.components.die(size=(2e3, 2e3), street_width=10)\n",
    "\n",
    "\n",
    "c = die_bad()\n",
    "c.show()\n",
    "c.plot()\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "source": [
    "**Solution** Do not use intermediate cells\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "\n",
    "\n",
    "@gf.cell\n",
    "def die_good():\n",
    "    c = gf.Component()\n",
    "    _ = c << gf.components.straight(length=10)\n",
    "    return gf.components.die(size=(2000,2000), street_width=10)\n",
    "\n",
    "\n",
    "c = die_good()\n",
    "c.plot()"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "custom_cell_magics": "kql"
  },
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
